package qmlt.dataset.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import qmlt.dataset.filter.CategoricalToNumericFilter;

public class ListUtils
{
    public static <T extends Number> int argmax(List<T> list)
    {
        if (list.size() <= 0)
        {
            return -1;
        }

        int iMax = 0;
        T vMax = list.get(0);
        for (int i = 1; i < list.size(); ++i)
        {
            T v = list.get(i);
            if (v.doubleValue() > vMax.doubleValue())
            {
                iMax = i;
                vMax = v;
            }
        }

        return iMax;
    }

    public static <T> boolean equals(List<T> l1, List<T> l2)
    {
        if (l1.size() != l2.size())
            return false;

        int n = l1.size();
        for (int i = 0; i < n; ++i)
        {
            if (!l1.get(i).equals(l2.get(i)))
            {
                return false;
            }
        }

        return true;
    }

    @SuppressWarnings("unchecked")
    public static <T1, T2> List<T2> convert(List<T1> list)
    {
        List<T2> rst = new ArrayList<T2>();
        for (T1 value : list)
        {
            rst.add((T2) value);
        }
        return rst;
    }

    public static <T extends Number> double mean(List<T> list)
    {
    		double ps = 0;
        double s = 0;
        for (T value : list)
        {
        		ps = s;
            s += value.doubleValue();
            if (Double.isNaN(s))
            {
            	System.err.println("previous s: " + ps + ", s: " + s + ", value: " + value);
            }
        }
        return s / list.size();
    }

    public static <T extends Number> double std(List<T> list, double mean)
    {
        double s = 0;
        for (T value : list)
        {
            double d = value.doubleValue() - mean;
            s += d * d;
        }
        return Math.sqrt(s / list.size());
    }

    public static <T> String toString(List<T> list, String delim)
    {
        String s = "";
        boolean first = true;
        for (T value : list)
        {
            s += String.format("%s%s", first ? "" : delim, value);
            first = false;
        }
        return s;
    }

    // FIXME this should go to a separate filter
    @Deprecated
    public static List<Object> convertNumericOutputToCategorical(List<Object> numericalOutput)
    {
        int n = numericalOutput.size();
        List<Object> rst = new ArrayList<Object>();

        List<Float> tmp = ListUtils.convert(numericalOutput);
        int t = ListUtils.argmax(tmp);
        for (int i = 0; i < n; ++i)
        {
            rst.add((float) CategoricalToNumericFilter.defaultNegValue);
        }
        rst.set(t, (float) CategoricalToNumericFilter.defaultPosValue);

        return rst;
    }

    /* it is required to use equals() to compare values in the list */
    public static <T> T findMajor(List<T> list)
    {
        if (list.size() <= 0)
            return null;

        class Item implements Comparable<Item>
        {
            public T   value;

            public int count;

            public Item(T value, int count)
            {
                this.value = value;
                this.count = count;
            }

            @Override
            public int compareTo(Item another)
            {
                return another.count - count;
            }
        }

        List<Item> the = new ArrayList<Item>();
        for (T v : list)
        {
            Item it = null;
            for (Item i : the)
            {
                if (i.value.equals(v))
                {
                    it = i;
                }
            }

            if (it == null)
            {
                the.add(new Item(v, 1));
            }
            else
            {
                it.count++;
            }
        }

        Collections.sort(the);
        return the.get(0).value;
    }

    public static <T> List<T> asList(T... values)
    {
        List<T> list = new ArrayList<T>();
        for (T v : values)
        {
            list.add(v);
        }
        return list;
    }

}
